#region File Description
//-----------------------------------------------------------------------------
// SinglePlayerScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using TestGame.GameLib;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Audio;
#endregion

namespace TestGame {
    /// <summary>
    /// A SinglePlayerScreen is a GameScreen that has only one player occuping 
    /// it. It has delegate Methods for updating, drawing, and handling input
    /// that allow for easy inheritence of this class to support multiple 
    /// players or other features.
    /// </summary>
    class SinglePlayerScreen : GameScreen {
        #region Fields

        /// <summary>
        /// The content used in the game for fonts, sprites, and audio.
        /// </summary>
        protected ContentManager content;

        /// <summary>
        /// The font to be used in displaying game information.
        /// </summary>
        protected SpriteFont font;

        /// <summary>
        /// The primary Player
        /// </summary>
        protected PlayingArea primaryPlayer;

        /// <summary>
        /// The music that should be played on LoadContent().
        /// </summary>
        protected Song music;

        /// <summary>
        /// The alpha setting for creating a smooth pause interupt.
        /// </summary>
        float pauseAlpha;

        /// <summary>
        /// Used to determine if the SinglePlayerScreen should pause the game
        /// if focus is lost on the Screen or Window. In singleplayer, it is 
        /// advised to have it set to true, but in multiplayer it is always 
        /// set to false.
        /// </summary>
        protected bool pauseOnLostFocus;

        #endregion

        #region Properties

        /// <summary>
        /// Used to determine if the SinglePlayerScreen should pause the game
        /// if focus is lost on the Screen or Window. In singleplayer, it is 
        /// advised to have it set to true, but in multiplayer it is always 
        /// set to false.
        /// </summary>
        public bool PauseOnLostFocus {
            get { return pauseOnLostFocus; }
            set { pauseOnLostFocus = value; }
        } 

        #endregion

        #region Constructor

        /// <summary>
        /// Constructors a SinglePlayerScreen with a single Player.
        /// </summary>
        public SinglePlayerScreen() {
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);
            pauseOnLostFocus = true;

            primaryPlayer = new PlayingArea();
        }

        #endregion

        #region Content Management

        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent() {
            if (content == null) {
                content = new ContentManager(ScreenManager.Game.Services, "Content");
            }

            PlayingArea.LoadContent(content);

            // Load some content for the Scene to use
            font = content.Load<SpriteFont>("gamefont");
            music = content.Load<Song>("music/Game");

            // Start the music
            MediaPlayer.Play(music);

            // Reset ElapsedTime after we load a bunch of assets
            ScreenManager.Game.ResetElapsedTime();
        }


        /// <summary>
        /// Unload graphics content used by the game, and stops any audio.
        /// </summary>
        public override void UnloadContent() {
            content.Unload();
            MediaPlayer.Stop();
        }

        #endregion

        #region Game Methods

        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen) {
            base.Update(gameTime, otherScreenHasFocus, false);

            // Gradually fade in or out depending on whether we are covered by the pause screen.
            if (coveredByOtherScreen) {
                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
            }
            else {
                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);
            }

            if (!coveredByOtherScreen || !pauseOnLostFocus) {
                ComponentUpdate(gameTime);
            }
        }

        /// <summary>
        /// The Method that will update all the components of the screen. 
        /// Sometimes this Method will not be called if the ScreenManager or
        /// the screen itself determines it should not be updated.
        /// </summary>
        /// <param name="time"></param>
        protected virtual void ComponentUpdate(GameTime time) {
            primaryPlayer.Update(time);

            if (primaryPlayer.IsGameOver()) {
                ScreenManager.AddScreen(new GameOverScreen(), ControllingPlayer);
            }
        }


        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input) {
            if (input == null) {
                throw new ArgumentNullException("input");
            }

            // Look up inputs for the active player profile.
            int playerIndex = (int)ControllingPlayer.Value;
            input.UpdateControllingPlayer(playerIndex);

            KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
            GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];

            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad. This requires us to keep track of
            // whether a gamepad was ever plugged in, because we don't want to pause
            // on PC if they are playing with a keyboard and have no gamepad at all!
            bool gamePadDisconnected = !gamePadState.IsConnected && input.GamePadWasConnected[playerIndex];

            if (input.IsPauseGame(ControllingPlayer) || gamePadDisconnected) {
                ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
            }
            else {
                ComponentHandleInput(input);
            }
        }

        /// <summary>
        /// Handles the input for the Components in the screen.
        /// </summary>
        /// <param name="input"></param>
        protected virtual void ComponentHandleInput(InputState input) {
            primaryPlayer.HandleInput(input);
        }


        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime) {
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target, Color.CornflowerBlue, 0, 0);
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

            spriteBatch.Begin();

            ComponentDraw(gameTime);

            spriteBatch.End();

            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0 || pauseAlpha > 0) {
                float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);

                ScreenManager.FadeBackBufferToBlack(alpha);
            }
        }

        /// <summary>
        /// Handles the drawing for all the components in the screen.
        /// </summary>
        /// <param name="time"></param>
        public virtual void ComponentDraw(GameTime time) {
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

            String helpText = GenerateHelpText();
            ParticleSystem.Draw(spriteBatch, content);
            spriteBatch.DrawString(font, helpText, new Vector2(500, 10), Color.Black);
            primaryPlayer.Draw(spriteBatch, content);
            
        }

        /// <summary>
        /// Generates the helpful text that will pop-up and inform the Player
        /// of the current key bindings.
        /// </summary>
        /// <returns></returns>
        protected virtual string GenerateHelpText() {
            String score = String.Format("Score: {0}\n", primaryPlayer.Score);
            String rotateCW = String.Format("Rotate Clockwise: {0}\n", KeySettings.Default.a);
            String rotateCCW = String.Format("Rotate Countclockwise: {0}\n", KeySettings.Default.b);
            String hardDrop = String.Format("Hard Drop: {0}\n", KeySettings.Default.c);
            String debugMode = String.Format("Debug Info: {0}\n", Keys.D);
            String addGray = String.Format("Add Gray Blobs: {0}\n", Keys.L);

            return score + rotateCW + rotateCCW + hardDrop + debugMode + addGray;
        }

        #endregion
    }
}
